package com.tbynet.jwp.framework.core;

import com.jfinal.log.Log;
import com.tbynet.jwp.framework.exception.RepositoryException;

import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * JWP数据仓储基类（异常处理增强版）
 * 提供可配置、高观测性、低耦合的异常处理模板，包含验证、性能监控、资源清理等通用功能
 * 异常体系：JwpException(RuntimeException) ← RepositoryException
 * 所有异常均为非受检异常，无需显式捕获或声明抛出
 *
 * @author 佰亿互联
 * @version 2.7
 * @since 2025-11-04
 */
public abstract class JwpRepository {

    /** 日志记录器 */
    protected final Log log = Log.getLog(getClass());

    /** 默认配置常量 */
    public static final long DEFAULT_WARN_THRESHOLD = 5000L;
    private static final long MAX_BACKOFF_TIME = 30000L;
    private static final long BASE_BACKOFF_TIME = 1000L;
    private static final int DEFAULT_MAX_RETRIES = 3;
    private static final Random RANDOM = new Random();

    // ========== 异常处理配置类（抽离配置，统一管理） ==========

    /**
     * 数据操作配置类
     * 支持链式配置，简化模板参数传递，集中管理重试、异常转换、回调等规则
     * 基于实际异常继承关系优化：重试仅针对特定RepositoryException场景，以下异常直接抛出不重试：
     * - 基础参数验证失败（REPOSITORY_PARAM_ERROR）
     *
     * @param <T> 操作返回值类型
     */
    protected static class OperationConfig<T> {
        private String operationName; // 操作名称（必填）
        private int maxRetries = DEFAULT_MAX_RETRIES; // 最大重试次数
        // 重试判断器：入参为RepositoryException（所有数据库异常的父类）
        private Predicate<RepositoryException> retryPredicate;
        private Consumer<RetryContext> beforeRetryCallback; // 重试前回调
        private Consumer<RetryContext> afterRetryCallback; // 重试后回调
        // 异常转换器：所有异常最终转为RepositoryException（保持体系一致性）
        private Function<Exception, RepositoryException> exceptionConverter;

        /**
         * 私有构造，通过静态工厂方法{@link #of(String)}创建
         */
        private OperationConfig(String operationName) {
            this.operationName = operationName;
            // 默认重试策略：仅重试SQL错误类型的RepositoryException
            this.retryPredicate = e -> "REPOSITORY_SQL_ERROR".equals(e.getCode()) && isRetryableRepositoryException(e);
            // 默认异常转换：保持异常体系一致性
            this.exceptionConverter = e -> {
                if (e instanceof RepositoryException) {
                    // 已在异常体系中，直接返回（保留原始链路）
                    return (RepositoryException) e;
                } else if (e instanceof InterruptedException) {
                    // 中断异常：包装为SQL错误类型的RepositoryException（可重试场景）
                    Thread.currentThread().interrupt(); // 恢复中断状态
                    return new RepositoryException(operationName + "执行被中断", e);
                } else {
                    // 其他受检/非受检异常：统一包装为SQL错误类型的RepositoryException
                    return new RepositoryException(buildErrorMsg(operationName), e);
                }
            };
        }

        /**
         * 静态工厂方法，创建操作配置实例（必填参数校验）
         *
         * @param operationName 操作名称（用于日志和异常信息）
         * @param <T>           操作返回值类型
         * @return 操作配置实例
         * @example
         * // 创建基础配置（使用默认重试/异常转换规则）
         * OperationConfig<User> config = OperationConfig.of("查询用户信息");
         */
        public static <T> OperationConfig<T> of(String operationName) {
            Objects.requireNonNull(operationName, "操作名称[operationName]不能为空");
            return new OperationConfig<>(operationName);
        }

        /**
         * 配置最大重试次数（默认3次，0表示不重试）
         *
         * @param maxRetries 最大重试次数（非负整数）
         * @return 当前配置实例（链式调用）
         * @throws IllegalArgumentException 当重试次数为负数时抛出
         * @example
         * // 配置最大重试2次（共执行3次）
         * OperationConfig.of("查询用户信息").maxRetries(2);
         */
        public OperationConfig<T> maxRetries(int maxRetries) {
            if (maxRetries < 0) {
                throw new IllegalArgumentException("最大重试次数[" + maxRetries + "]不能为负数");
            }
            this.maxRetries = maxRetries;
            return this;
        }

        /**
         * 配置重试判断器（自定义哪些RepositoryException需要重试）
         * 注意：参数验证异常（REPOSITORY_PARAM_ERROR）不建议重试，SQL错误（REPOSITORY_SQL_ERROR）可根据场景决定
         *
         * @param retryPredicate 重试条件判断函数（入参为RepositoryException）
         * @return 当前配置实例（链式调用）
         * @example
         * // 仅重试错误码为REPOSITORY_SQL_ERROR且包含超时的异常
         * OperationConfig.of("调用第三方接口")
         *     .retryWhen(e -> "REPOSITORY_SQL_ERROR".equals(e.getCode()) &&
         *             e.getMessage().contains("timeout"));
         */
        public OperationConfig<T> retryWhen(Predicate<RepositoryException> retryPredicate) {
            this.retryPredicate = Objects.requireNonNull(retryPredicate, "重试判断器[retryPredicate]不能为空");
            return this;
        }

        /**
         * 配置重试前回调（重试等待前执行，如告警、资源清理）
         *
         * @param callback 回调函数（入参为重试上下文{@link RetryContext}）
         * @return 当前配置实例（链式调用）
         * @example
         * // 重试前发送告警（第2次重试时触发）
         * OperationConfig.of("查询订单")
         *     .beforeRetry(ctx -> {
         *         if (ctx.getCurrentAttempt() >= 2) {
         *             alertService.send("订单查询重试次数超标", ctx.getCause().getMessage());
         *         }
         *     });
         */
        public OperationConfig<T> beforeRetry(Consumer<RetryContext> callback) {
            this.beforeRetryCallback = callback;
            return this;
        }

        /**
         * 配置重试后回调（重试等待后执行，如统计重试次数）
         *
         * @param callback 回调函数（入参为重试上下文{@link RetryContext}）
         * @return 当前配置实例（链式调用）
         * @example
         * // 重试后记录统计信息
         * OperationConfig.of("更新库存")
         *     .afterRetry(ctx -> {
         *         statsService.recordRetry("inventory_update", ctx.getCurrentAttempt());
         *     });
         */
        public OperationConfig<T> afterRetry(Consumer<RetryContext> callback) {
            this.afterRetryCallback = callback;
            return this;
        }

        /**
         * 配置异常转换器（自定义异常包装规则，必须返回RepositoryException）
         * 确保异常体系一致性，便于全局异常处理器统一处理
         *
         * @param converter 异常转换函数（入参为原始异常，返回RepositoryException）
         * @return 当前配置实例（链式调用）
         * @example
         * // 将超时异常转换为SQL错误类型，资源不存在转换为参数错误类型
         * OperationConfig.of("用户登录")
         *     .convertException(e -> {
         *         if (e instanceof RepositoryException) {
         *             return (RepositoryException) e;
         *         }
         *         return e instanceof TimeoutException
         *             ? new RepositoryException("登录超时，请重试", e) // SQL错误类型
         *             : new RepositoryException("用户不存在"); // 参数错误类型
         *     });
         */
        public OperationConfig<T> convertException(Function<Exception, RepositoryException> converter) {
            this.exceptionConverter = Objects.requireNonNull(converter, "异常转换器[exceptionConverter]不能为空");
            return this;
        }

        /**
         * 构建错误消息（内部辅助方法）
         */
        private static String buildErrorMsg(String operationName) {
            return String.format("操作[%s]执行失败", operationName);
        }

        /**
         * 判断是否为可重试的RepositoryException（内部辅助方法）
         * 默认可重试场景：超时、连接异常、临时SQL异常等
         */
        private static boolean isRetryableRepositoryException(RepositoryException e) {
            String exceptionName = e.getClass().getName();
            return exceptionName.contains("TimeoutException")
                    || exceptionName.contains("ConnectionException")
                    || exceptionName.contains("SocketException")
                    || exceptionName.contains("SQLTransientException")
                    || (e.getCause() != null && isRetryableCause(e.getCause()));
        }

        /**
         * 判断异常原因是否可重试（内部辅助方法）
         */
        private static boolean isRetryableCause(Throwable cause) {
            if (cause == null) {
                return false;
            }
            String causeName = cause.getClass().getName();
            return causeName.contains("TimeoutException")
                    || causeName.contains("ConnectionException")
                    || causeName.contains("SocketException");
        }
    }

    /**
     * 重试上下文类
     * 封装重试过程中的关键信息，传递给回调函数
     * 异常类型统一为RepositoryException（仅该类支持重试）
     */
    protected static class RetryContext {
        private final String operationName;
        private final int currentAttempt; // 当前重试次数（从1开始）
        private final int maxRetries;
        private final long backoffTime; // 本次退避时间（ms）
        private final RepositoryException cause; // 触发重试的异常（仅RepositoryException）

        /**
         * 构造重试上下文
         *
         * @param operationName 操作名称
         * @param currentAttempt 当前重试次数（1表示第1次重试）
         * @param maxRetries 最大重试次数
         * @param backoffTime 退避时间（ms）
         * @param cause 触发重试的异常（RepositoryException）
         */
        public RetryContext(String operationName, int currentAttempt, int maxRetries, long backoffTime, RepositoryException cause) {
            this.operationName = operationName;
            this.currentAttempt = currentAttempt;
            this.maxRetries = maxRetries;
            this.backoffTime = backoffTime;
            this.cause = cause;
        }

        /**
         * 获取操作名称
         */
        public String getOperationName() { return operationName; }

        /**
         * 获取当前重试次数（从1开始，1表示第1次重试）
         */
        public int getCurrentAttempt() { return currentAttempt; }

        /**
         * 获取最大重试次数
         */
        public int getMaxRetries() { return maxRetries; }

        /**
         * 获取本次退避时间（ms）
         */
        public long getBackoffTime() { return backoffTime; }

        /**
         * 获取触发重试的异常（RepositoryException）
         */
        public RepositoryException getCause() { return cause; }
    }

    // ========== 核心异常处理模板 ==========

    /**
     * 执行数据访问操作（带异常处理+可配置重试）
     * 推荐使用：支持自定义重试策略、异常转换、回调函数，灵活性最高
     * 重试逻辑：根据RepositoryException错误码区分处理：
     * - REPOSITORY_PARAM_ERROR（参数验证错误）：直接抛出，不重试
     * - REPOSITORY_SQL_ERROR（SQL操作错误）：根据重试策略判断是否重试
     * 数据库操作执行
     * ├── 无异常 → 返回结果
     * ├── 抛出异常 → 统一转换为RepositoryException
     * │   ├── 是REPOSITORY_PARAM_ERROR → 输出ERROR日志，直接抛出
     * │   ├── 是REPOSITORY_SQL_ERROR → 检查是否需要重试
     * │       ├── 不需要重试 → 输出ERROR日志，直接抛出
     * │       ├── 需要重试 → 执行回调→退避等待→重试
     * │           ├── 重试成功 → 返回结果
     * │           ├── 重试耗尽 → 输出ERROR日志，抛出最终RepositoryException
     *
     * @param config    操作配置（通过{@link OperationConfig#of(String)}创建）
     * @param operation 具体数据库操作（无参数，返回操作结果）
     * @param <T>       操作返回值类型
     * @return 操作执行结果
     * @throws RepositoryException 数据库异常（直接抛出，非受检）
     * @example
     * // 完整示例：查询用户信息，重试2次，仅重试超时类SQL异常
     * User user = executeDataOperation(
     *     OperationConfig.of("查询用户信息")
     *         .maxRetries(2) // 重试2次（共3次执行）
     *         .retryWhen(e -> "REPOSITORY_SQL_ERROR".equals(e.getCode()) &&
     *                 e.getCause() instanceof TimeoutException)
     *         .beforeRetry(ctx -> {
     *             if (ctx.getCurrentAttempt() == 2) {
     *                 alertService.send("用户查询重试2次", ctx.getCause().getMessage());
     *             }
     *         })
     *         .convertException(e -> {
     *             if (e instanceof RepositoryException) {
     *                 return new RepositoryException("用户ID：" + userId + "不存在");
     *             }
     *             return new RepositoryException("用户查询失败（ID：" + userId + "）", e);
     *         }),
     *     () -> userDao.queryById(userId) // 实际数据库操作（可能抛出RepositoryException）
     * );
     */
    protected <T> T executeDataOperation(OperationConfig<T> config, Supplier<T> operation) {
        Objects.requireNonNull(config, "操作配置[OperationConfig]不能为空");
        Objects.requireNonNull(operation, "操作回调函数[operation]不能为空");

        String operationName = config.operationName;
        int maxRetries = config.maxRetries;
        RepositoryException lastRetryException = null;

        // 循环执行（包含首次执行，attempt从0开始）
        for (int attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                log.debug("[%s]开始执行（第%s次，最大%s次）", operationName, attempt + 1, maxRetries);
                T result = operation.get();
                log.debug("[%s]第%s次执行成功", operationName, attempt + 1);
                return result;
            } catch (Exception e) {
                // 统一转换为RepositoryException（保持异常体系一致性）
                RepositoryException rex = config.exceptionConverter.apply(e);
                lastRetryException = rex;

                // 根据错误码判断是否需要重试
                boolean needRetry = attempt < maxRetries && config.retryPredicate.test(rex);
                if (needRetry) {
                    // 计算退避时间（指数退避+随机抖动）
                    long backoffTime = calculateBackoff(attempt);
                    RetryContext context = new RetryContext(operationName, attempt + 1, maxRetries, backoffTime, rex);

                    // 执行重试前回调
                    if (config.beforeRetryCallback != null) {
                        try {
                            config.beforeRetryCallback.accept(context);
                        } catch (Exception ex) {
                            log.error("[%s]重试前回调执行失败", operationName, ex);
                        }
                    }

                    // 记录重试日志
                    log.warn("[%s]第%s次执行失败（可重试，错误码：%s），原因：%s，将在%sms后进行第%s次重试",
                            operationName, attempt + 1, rex.getCode(), rex.getMessage(), backoffTime, attempt + 2, rex);

                    // 退避等待（中断异常已在转换器中处理）
                    try {
                        Thread.sleep(backoffTime);
                    } catch (InterruptedException ie) {
                        // 此处理论上不会触发（转换器已处理），仅做兜底
                        Thread.currentThread().interrupt();
                        RepositoryException interruptEx = new RepositoryException(operationName + "重试等待被中断", ie);
                        log.error("[%s]重试等待被中断，终止执行", operationName, interruptEx);
                        throw interruptEx;
                    }

                    // 执行重试后回调
                    if (config.afterRetryCallback != null) {
                        try {
                            config.afterRetryCallback.accept(context);
                        } catch (Exception ex) {
                            log.error("[%s]重试后回调执行失败", operationName, ex);
                        }
                    }
                } else {
                    // 不需要重试：直接抛出RepositoryException
                    log.error("[%s]数据访问失败（不重试，错误码：%s）：%s",
                            operationName, rex.getCode(), rex.getMessage(), rex);
                    throw rex;
                }
            }
        }

        // 重试耗尽：抛出最终异常（明确重试次数）
        RepositoryException finalEx = new RepositoryException(
                String.format("[%s]重试%d次后仍失败", operationName, maxRetries), lastRetryException);
        log.error("[%s]重试耗尽，终止执行（错误码：%s）：%s",
                operationName, finalEx.getCode(), finalEx.getMessage(), finalEx);
        throw finalEx;
    }

    /**
     * 简化版：执行数据访问操作（无返回值，使用默认配置）
     * 兼容原有使用习惯，适用于无需自定义配置的场景
     *
     * @param operationName 操作名称（用于日志和异常信息）
     * @param operation     具体数据库操作（无参数、无返回值）
     * @throws RepositoryException 数据库异常（直接抛出）
     * @example
     * // 保存用户信息（无返回值，使用默认重试策略）
     * executeDataOperation("保存用户信息", () -> {
     *     userDao.insert(user); // 数据操作失败抛出RepositoryException
     * });
     */
    protected void executeDataOperation(String operationName, Runnable operation) {
        executeDataOperation(operationName, () -> {
            operation.run();
            return null;
        });
    }

    /**
     * 简化版：执行数据访问操作（使用默认配置）
     * 兼容原有使用习惯，适用于无需自定义重试/异常规则的场景
     *
     * @param operationName 操作名称（用于日志和异常信息）
     * @param operation     具体数据库操作（无参数，返回操作结果）
     * @param <T>           操作返回值类型
     * @return 操作执行结果
     * @throws RepositoryException 数据库异常（直接抛出）
     * @example
     * // 查询用户信息（使用默认配置：重试3次，仅SQL错误可重试）
     * User user = executeDataOperation("查询用户信息", () -> {
     *     User user = userDao.queryById(userId);
     *     return user;
     * });
     */
    protected <T> T executeDataOperation(String operationName, Supplier<T> operation) {
        return executeDataOperation(OperationConfig.of(operationName), operation);
    }

    /**
     * 简化版：执行数据访问操作（自定义重试次数，其他使用默认配置）
     * 兼容原有`executeDataOperationWithRetry`方法，降低迁移成本
     *
     * @param operationName 操作名称（用于日志和异常信息）
     * @param operation     具体数据库操作（无参数，返回操作结果）
     * @param maxRetries    最大重试次数（0表示不重试）
     * @param <T>           操作返回值类型
     * @return 操作执行结果
     * @throws RepositoryException 数据库异常（直接抛出）
     * @example
     * // 查询订单信息，自定义重试1次（共2次执行）
     * Order order = executeDataOperationWithRetry("查询订单信息", () -> {
     *     Order order = orderDao.queryById(orderId);
     *     if (order == null) {
     *         throw new RepositoryException("订单ID：" + orderId + "不存在");
     *     }
     *     return order;
     * }, 1);
     */
    protected <T> T executeDataOperationWithRetry(String operationName, Supplier<T> operation, int maxRetries) {
        // 显式指定OperationConfig的泛型为T，与Supplier<T>和方法返回值泛型一致
        OperationConfig<T> config = OperationConfig.<T>of(operationName).maxRetries(maxRetries);
        return executeDataOperation(config, operation);
    }

    // ========== 基础验证方法 ==========

    /**
     * 验证对象不为空（使用默认错误消息）
     * 验证失败抛出参数类型RepositoryException（REPOSITORY_PARAM_ERROR，不重试）
     *
     * @param obj       待验证对象
     * @param fieldName 字段名称（用于错误信息）
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证用户对象不为空
     * validateNotNull(user, "user"); // 若user为null，抛出RepositoryException("user不能为空")
     */
    protected void validateNotNull(Object obj, String fieldName) {
        validateNotNull(obj, fieldName, fieldName + "不能为空");
    }

    /**
     * 验证对象不为空（支持自定义错误消息）
     *
     * @param obj       待验证对象
     * @param fieldName 字段名称（用于错误信息）
     * @param errorMsg  自定义错误消息
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证用户对象不为空（自定义错误消息）
     * validateNotNull(user, "user", "用户信息不能为空，请传入有效用户对象");
     */
    protected void validateNotNull(Object obj, String fieldName, String errorMsg) {
        if (obj == null) {
            throw new RepositoryException(fieldName + errorMsg);
        }
    }

    /**
     * 验证字符串不为空（非null且非空白，使用默认错误消息）
     *
     * @param str       待验证字符串
     * @param fieldName 字段名称（用于错误信息）
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证用户名不为空
     * validateNotBlank(user.getName(), "userName"); // 若名称为null/空白，抛出RepositoryException
     */
    protected void validateNotBlank(String str, String fieldName) {
        validateNotBlank(str, fieldName, fieldName + "不能为空（不能为null或空白字符串）");
    }

    /**
     * 验证字符串不为空（非null且非空白，支持自定义错误消息）
     *
     * @param str       待验证字符串
     * @param fieldName 字段名称（用于错误信息）
     * @param errorMsg  自定义错误消息
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证用户名不为空（自定义错误消息）
     * validateNotBlank(user.getName(), "userName", "用户名不能为空，请输入至少1个非空白字符");
     */
    protected void validateNotBlank(String str, String fieldName, String errorMsg) {
        if (str == null || str.trim().isEmpty()) {
            throw new RepositoryException(fieldName + errorMsg);
        }
    }

    /**
     * 验证集合不为空（非null且非空集合，使用默认错误消息）
     *
     * @param collection 待验证集合
     * @param fieldName  字段名称（用于错误信息）
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证订单列表不为空
     * validateCollectionNotEmpty(orderList, "orderList"); // 若集合为null/空，抛出RepositoryException
     */
    protected void validateCollectionNotEmpty(Collection<?> collection, String fieldName) {
        validateCollectionNotEmpty(collection, fieldName, fieldName + "不能为空（不能为null或空集合）");
    }

    /**
     * 验证集合不为空（非null且非空集合，支持自定义错误消息）
     *
     * @param collection 待验证集合
     * @param fieldName  字段名称（用于错误信息）
     * @param errorMsg   自定义错误消息
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证订单列表不为空（自定义错误消息）
     * validateCollectionNotEmpty(orderList, "orderList", "订单列表不能为空，请至少选择1个订单");
     */
    protected void validateCollectionNotEmpty(Collection<?> collection, String fieldName, String errorMsg) {
        if (collection == null || collection.isEmpty()) {
            throw new RepositoryException(fieldName + errorMsg);
        }
    }

    /**
     * 验证Map不为空（非null且非空Map，使用默认错误消息）
     *
     * @param map       待验证Map
     * @param fieldName 字段名称（用于错误信息）
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证参数Map不为空
     * validateMapNotEmpty(paramMap, "paramMap"); // 若Map为null/空，抛出RepositoryException
     */
    protected void validateMapNotEmpty(Map<?, ?> map, String fieldName) {
        validateMapNotEmpty(map, fieldName, fieldName + "不能为空（不能为null或空Map）");
    }

    /**
     * 验证Map不为空（非null且非空Map，支持自定义错误消息）
     *
     * @param map       待验证Map
     * @param fieldName 字段名称（用于错误信息）
     * @param errorMsg  自定义错误消息
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证参数Map不为空（自定义错误消息）
     * validateMapNotEmpty(paramMap, "paramMap", "请求参数不能为空，请传入有效参数键值对");
     */
    protected void validateMapNotEmpty(Map<?, ?> map, String fieldName, String errorMsg) {
        if (map == null || map.isEmpty()) {
            throw new RepositoryException(fieldName + errorMsg);
        }
    }

    /**
     * 验证数值范围（包含边界，使用默认错误消息）
     * 支持所有Number子类（Integer、Long、Double等），避免类型溢出
     *
     * @param value     待验证数值
     * @param fieldName 字段名称（用于错误信息）
     * @param min       最小值（包含）
     * @param max       最大值（包含）
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证年龄在18-60之间
     * validateNumberRange(user.getAge(), "age", 18, 60);
     * // 验证金额在0-10000之间（支持Double）
     * validateNumberRange(order.getAmount(), "amount", 0, 10000);
     */
    protected void validateNumberRange(Number value, String fieldName, long min, long max) {
        validateNumberRange(value, fieldName, min, max,
                String.format("%s必须在%d到%d之间（包含边界）", fieldName, min, max));
    }

    /**
     * 验证数值范围（包含边界，支持自定义错误消息）
     *
     * @param value     待验证数值
     * @param fieldName 字段名称（用于错误信息）
     * @param min       最小值（包含）
     * @param max       最大值（包含）
     * @param errorMsg  自定义错误消息
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证年龄在18-60之间（自定义错误消息）
     * validateNumberRange(user.getAge(), "age", 18, 60, "年龄必须为18-60岁的成年人");
     */
    protected void validateNumberRange(Number value, String fieldName, long min, long max, String errorMsg) {
        validateNotNull(value, fieldName);
        double num = value.doubleValue();
        if (num < min || num > max) {
            throw new RepositoryException(fieldName + errorMsg);
        }
    }

    /**
     * 验证条件为真（指定字段名，支持自定义错误消息）
     *
     * @param condition 待验证条件（true表示验证通过）
     * @param fieldName 字段名称（用于错误信息）
     * @param errorMsg  自定义错误消息
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证密码长度>=6位
     * validateCondition(user.getPassword().length() >= 6, "password", "密码长度不能少于6位");
     */
    protected void validateCondition(boolean condition, String fieldName, String errorMsg) {
        if (!condition) {
            throw new RepositoryException(fieldName + errorMsg);
        }
    }

    /**
     * 验证条件为真（默认字段名，兼容原有接口）
     *
     * @param condition 待验证条件（true表示验证通过）
     * @param message   错误消息
     * @throws RepositoryException 验证失败时抛出（非受检异常）
     * @example
     * // 验证订单状态为待支付
     * validateCondition(order.getStatus() == OrderStatus.PENDING_PAY, "订单状态必须为待支付才能取消");
     */
    protected void validateCondition(boolean condition, String message) {
        validateCondition(condition, "VALIDATION_FAILED", message);
    }

    // ========== 性能监控工具方法 ==========

    /**
     * 执行操作并记录耗时（使用默认警告阈值：{@link #DEFAULT_WARN_THRESHOLD}=5000ms）
     * 超过阈值输出WARN日志，否则输出DEBUG日志（需开启DEBUG级别）
     *
     * @param operationName 操作名称（用于日志）
     * @param operation     具体数据库操作（无参数，返回操作结果）
     * @param <T>           操作返回值类型
     * @return 操作执行结果
     * @throws RepositoryException 数据库操作中抛出的非受检异常
     * @example
     * // 查询商品列表并记录耗时（默认阈值5000ms）
     * List<Product> products = executeWithTiming("查询商品列表", () -> productDao.listAll());
     */
    protected <T> T executeWithTiming(String operationName, Supplier<T> operation) {
        return executeWithTiming(operationName, operation, DEFAULT_WARN_THRESHOLD);
    }

    /**
     * 执行操作并记录耗时（支持自定义警告阈值）
     *
     * @param operationName 操作名称（用于日志）
     * @param operation     具体数据库操作（无参数，返回操作结果）
     * @param warnThreshold 耗时警告阈值（ms），超过该值输出WARN日志
     * @param <T>           操作返回值类型
     * @return 操作执行结果
     * @throws IllegalArgumentException 当阈值为负数时抛出（非受检异常）
     * @throws RepositoryException 数据库操作中抛出的非受检异常
     * @example
     * // 调用第三方接口并记录耗时（自定义阈值3000ms）
     * String result = executeWithTiming("调用物流接口", () -> logisticsClient.queryTrace(orderNo), 3000);
     */
    protected <T> T executeWithTiming(String operationName, Supplier<T> operation, long warnThreshold) {
        validateNotBlank(operationName, "operationName");
        Objects.requireNonNull(operation, "操作回调函数[operation]不能为空");
        if (warnThreshold < 0) {
            throw new IllegalArgumentException("性能警告阈值[" + warnThreshold + "]不能为负数");
        }

        long startTime = System.currentTimeMillis();
        try {
            return operation.get();
        } finally {
            logOperationCost(operationName, startTime, warnThreshold);
        }
    }

    /**
     * 执行操作并记录耗时（无返回值，支持自定义警告阈值）
     *
     * @param operationName 操作名称（用于日志）
     * @param operation     具体数据库操作（无参数、无返回值）
     * @param warnThreshold 耗时警告阈值（ms）
     * @throws IllegalArgumentException 当阈值为负数时抛出（非受检异常）
     * @throws RepositoryException 数据库操作中抛出的非受检异常
     * @example
     * // 同步库存并记录耗时（阈值2000ms）
     * executeWithTiming("同步库存数据", () -> inventoryService.syncAll(), 2000);
     */
    protected void executeWithTiming(String operationName, Runnable operation, long warnThreshold) {
        Objects.requireNonNull(operation, "操作回调函数[operation]不能为空");
        executeWithTiming(operationName, () -> {
            operation.run();
            return null;
        }, warnThreshold);
    }

    /**
     * 执行操作并记录耗时（无返回值，使用默认警告阈值）
     *
     * @param operationName 操作名称（用于日志）
     * @param operation     具体数据库操作（无参数、无返回值）
     * @throws RepositoryException 数据库操作中抛出的非受检异常
     * @example
     * // 导出数据并记录耗时（默认阈值5000ms）
     * executeWithTiming("导出订单数据", () -> exportService.exportOrders(orderDate));
     */
    protected void executeWithTiming(String operationName, Runnable operation) {
        executeWithTiming(operationName, operation, DEFAULT_WARN_THRESHOLD);
    }

    /**
     * 记录操作耗时（内部工具方法，不对外暴露）
     * 按耗时分级输出日志：超过阈值→WARN，否则→DEBUG
     */
    private void logOperationCost(String operationName, long startTime, long warnThreshold) {
        long cost = System.currentTimeMillis() - startTime;
        if (cost > warnThreshold) {
            log.warn("[性能警告] 操作[%s]执行耗时过长：%sms（阈值：%sms）", operationName, cost, warnThreshold);
        } else if (log.isDebugEnabled()) {
            log.debug("[性能监控] 操作[%s]执行完成，耗时：%sms", operationName, cost);
        }
    }

    // ========== 资源清理工具方法 ==========

    /**
     * 安全关闭多个可关闭资源（批量关闭，忽略关闭异常）
     * 支持可变参数，无需手动判断资源是否为null
     *
     * @param closeables 待关闭的资源（实现AutoCloseable接口）
     * @example
     * // 批量关闭数据库连接、Statement、ResultSet
     * closeQuietly(connection, statement, resultSet);
     * // 批量关闭文件流、网络流
     * closeQuietly(fileInputStream, socketInputStream);
     */
    protected void closeQuietly(AutoCloseable... closeables) {
        if (closeables == null) {
            return;
        }
        for (AutoCloseable closeable : closeables) {
            if (closeable != null) {
                try {
                    closeable.close();
                } catch (Exception e) {
                    log.warn("安全关闭资源时发生异常", e);
                }
            }
        }
    }

    /**
     * 安全关闭单个可关闭资源（兼容原有接口）
     *
     * @param closeable 待关闭的资源（实现AutoCloseable接口）
     * @example
     * // 关闭文件输出流
     * FileOutputStream fos = new FileOutputStream("test.txt");
     * try {
     *     fos.write(data);
     * } finally {
     *     closeQuietly(fos);
     * }
     */
    protected void closeQuietly(AutoCloseable closeable) {
        closeQuietly(new AutoCloseable[]{closeable});
    }

    // ========== 辅助方法 ==========

    /**
     * 计算退避时间（指数退避+随机抖动）
     * 公式：baseTime = min(1000 * 2^attempt, 30000)，再叠加±10%随机抖动
     * 避免多实例同时重试导致的峰值压力（重试风暴）
     *
     * @param attempt 当前尝试次数（从0开始，0表示首次失败）
     * @return 退避时间（ms）
     */
    private long calculateBackoff(int attempt) {
        double backoff = BASE_BACKOFF_TIME * Math.pow(2, attempt);
        long baseTime = (long) Math.min(backoff, MAX_BACKOFF_TIME);
        // 随机抖动：±10%，分散重试峰值
        return (long) (baseTime * (0.9 + RANDOM.nextDouble() * 0.2));
    }
}